{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#hide\n",
    "#skip\n",
    "! [ -e /content ] && pip install -Uqq fastai  # upgrade fastai on colab"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#default_exp vision.widgets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "from fastai.torch_basics import *\n",
    "from fastai.data.all import *\n",
    "from fastai.vision.core import *\n",
    "from ipywidgets import HBox,VBox,widgets,Button,Checkbox,Dropdown,Layout,Box,Output,Label,FileUpload"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#hide\n",
    "from nbdev.showdoc import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "_all_ = ['HBox','VBox','widgets','Button','Checkbox','Dropdown','Layout','Box','Output','Label','FileUpload']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Vision widgets\n",
    "\n",
    "> ipywidgets for images"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "@patch\n",
    "def __getitem__(self:Box, i): return self.children[i]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "def widget(im, *args, **layout):\n",
    "    \"Convert anything that can be `display`ed by IPython into a widget\"\n",
    "    o = Output(layout=merge(*args, layout))\n",
    "    with o: display(im)\n",
    "    return o"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "8d302ee695f7418a8553ee6d59f30e61",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(HTML(value='Puppy'), Output(layout=Layout(max_width='192px'))))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "im = Image.open('images/puppy.jpg').to_thumb(256,512)\n",
    "VBox([widgets.HTML('Puppy'),\n",
    "      widget(im, max_width=\"192px\")])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "def _update_children(change):\n",
    "    for o in change['owner'].children:\n",
    "        if not o.layout.flex: o.layout.flex = '0 0 auto'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "def carousel(children=(), **layout):\n",
    "    \"A horizontally scrolling carousel\"\n",
    "    def_layout = dict(overflow='scroll hidden', flex_flow='row', display='flex')\n",
    "    res = Box([], layout=merge(def_layout, layout))\n",
    "    res.observe(_update_children, names='children')\n",
    "    res.children = children\n",
    "    return res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "6324eef02b6747f581d38c4cf1ca6bdb",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Box(children=(VBox(children=(Output(layout=Layout(max_width='192px')), Button(description='click', style=Butto…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ts = [VBox([widget(im, max_width='192px'), Button(description='click')])\n",
    "      for o in range(3)]\n",
    "\n",
    "carousel(ts, width='450px')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "def _open_thumb(fn, h, w): return Image.open(fn).to_thumb(h, w).convert('RGBA')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class ImagesCleaner:\n",
    "    \"A widget that displays all images in `fns` along with a `Dropdown`\"\n",
    "    def __init__(self, opts=(), height=128, width=256, max_n=30):\n",
    "        opts = ('<Keep>', '<Delete>')+tuple(opts)\n",
    "        store_attr('opts,height,width,max_n')\n",
    "        self.widget = carousel(width='100%')\n",
    "\n",
    "    def set_fns(self, fns):\n",
    "        self.fns = L(fns)[:self.max_n]\n",
    "        ims = parallel(_open_thumb, self.fns, h=self.height, w=self.width, progress=False,\n",
    "                       n_workers=min(len(self.fns)//10,defaults.cpus))\n",
    "        self.widget.children = [VBox([widget(im, height=f'{self.height}px'), Dropdown(\n",
    "            options=self.opts, layout={'width': 'max-content'})]) for im in ims]\n",
    "\n",
    "    def _ipython_display_(self): display(self.widget)\n",
    "    def values(self): return L(self.widget.children).itemgot(1).attrgot('value')\n",
    "    def delete(self): return self.values().argwhere(eq('<Delete>'))\n",
    "    def change(self):\n",
    "        idxs = self.values().argwhere(not_(in_(['<Delete>','<Keep>'])))\n",
    "        return idxs.zipwith(self.values()[idxs])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f8dce8ef9f7049fcb8f90ad3c258c3be",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Box(children=(VBox(children=(Output(layout=Layout(height='128px')), Dropdown(layout=Layout(width='max-content'…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fns = get_image_files('images')\n",
    "w = ImagesCleaner(('A','B'))\n",
    "w.set_fns(fns)\n",
    "w"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((#0) [], (#0) [])"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "w.delete(),w.change()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "def _get_iw_info(learn, ds_idx=0):\n",
    "    dl = learn.dls[ds_idx].new(shuffle=False, drop_last=False)\n",
    "    probs,targs,preds,losses = learn.get_preds(dl=dl, with_input=False, with_loss=True, with_decoded=True)\n",
    "    targs = [dl.vocab[t] for t in targs]\n",
    "    return L([dl.dataset.items,targs,losses]).zip()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "@delegates(ImagesCleaner)\n",
    "class ImageClassifierCleaner(GetAttr):\n",
    "    \"A widget that provides an `ImagesCleaner` with a CNN `Learner`\"\n",
    "    def __init__(self, learn, **kwargs):\n",
    "        vocab = learn.dls.vocab\n",
    "        self.default = self.iw = ImagesCleaner(vocab, **kwargs)\n",
    "        self.dd_cats = Dropdown(options=vocab)\n",
    "        self.dd_ds   = Dropdown(options=('Train','Valid'))\n",
    "        self.iwis = _get_iw_info(learn,0),_get_iw_info(learn,1)\n",
    "        self.dd_ds.observe(self.on_change_ds, 'value')\n",
    "        self.dd_cats.observe(self.on_change_ds, 'value')\n",
    "        self.on_change_ds()\n",
    "        self.widget = VBox([self.dd_cats, self.dd_ds, self.iw.widget])\n",
    "\n",
    "    def _ipython_display_(self): display(self.widget)\n",
    "    def on_change_ds(self, change=None):\n",
    "        info = L(o for o in self.iwis[self.dd_ds.index] if o[1]==self.dd_cats.value)\n",
    "        self.iw.set_fns(info.sorted(2, reverse=True).itemgot(0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Export -"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Converted 00_torch_core.ipynb.\n",
      "Converted 01_layers.ipynb.\n",
      "Converted 01a_losses.ipynb.\n",
      "Converted 02_data.load.ipynb.\n",
      "Converted 03_data.core.ipynb.\n",
      "Converted 04_data.external.ipynb.\n",
      "Converted 05_data.transforms.ipynb.\n",
      "Converted 06_data.block.ipynb.\n",
      "Converted 07_vision.core.ipynb.\n",
      "Converted 08_vision.data.ipynb.\n",
      "Converted 09_vision.augment.ipynb.\n",
      "Converted 09b_vision.utils.ipynb.\n",
      "Converted 09c_vision.widgets.ipynb.\n",
      "Converted 10_tutorial.pets.ipynb.\n",
      "Converted 10b_tutorial.albumentations.ipynb.\n",
      "Converted 11_vision.models.xresnet.ipynb.\n",
      "Converted 12_optimizer.ipynb.\n",
      "Converted 13_callback.core.ipynb.\n",
      "Converted 13a_learner.ipynb.\n",
      "Converted 13b_metrics.ipynb.\n",
      "Converted 14_callback.schedule.ipynb.\n",
      "Converted 14a_callback.data.ipynb.\n",
      "Converted 15_callback.hook.ipynb.\n",
      "Converted 15a_vision.models.unet.ipynb.\n",
      "Converted 16_callback.progress.ipynb.\n",
      "Converted 17_callback.tracker.ipynb.\n",
      "Converted 18_callback.fp16.ipynb.\n",
      "Converted 18a_callback.training.ipynb.\n",
      "Converted 18b_callback.preds.ipynb.\n",
      "Converted 19_callback.mixup.ipynb.\n",
      "Converted 20_interpret.ipynb.\n",
      "Converted 20a_distributed.ipynb.\n",
      "Converted 21_vision.learner.ipynb.\n",
      "Converted 22_tutorial.imagenette.ipynb.\n",
      "Converted 23_tutorial.vision.ipynb.\n",
      "Converted 24_tutorial.siamese.ipynb.\n",
      "Converted 24_vision.gan.ipynb.\n",
      "Converted 30_text.core.ipynb.\n",
      "Converted 31_text.data.ipynb.\n",
      "Converted 32_text.models.awdlstm.ipynb.\n",
      "Converted 33_text.models.core.ipynb.\n",
      "Converted 34_callback.rnn.ipynb.\n",
      "Converted 35_tutorial.wikitext.ipynb.\n",
      "Converted 36_text.models.qrnn.ipynb.\n",
      "Converted 37_text.learner.ipynb.\n",
      "Converted 38_tutorial.text.ipynb.\n",
      "Converted 39_tutorial.transformers.ipynb.\n",
      "Converted 40_tabular.core.ipynb.\n",
      "Converted 41_tabular.data.ipynb.\n",
      "Converted 42_tabular.model.ipynb.\n",
      "Converted 43_tabular.learner.ipynb.\n",
      "Converted 44_tutorial.tabular.ipynb.\n",
      "Converted 45_collab.ipynb.\n",
      "Converted 46_tutorial.collab.ipynb.\n",
      "Converted 50_tutorial.datablock.ipynb.\n",
      "Converted 60_medical.imaging.ipynb.\n",
      "Converted 61_tutorial.medical_imaging.ipynb.\n",
      "Converted 65_medical.text.ipynb.\n",
      "Converted 70_callback.wandb.ipynb.\n",
      "Converted 71_callback.tensorboard.ipynb.\n",
      "Converted 72_callback.neptune.ipynb.\n",
      "Converted 73_callback.captum.ipynb.\n",
      "Converted 74_callback.azureml.ipynb.\n",
      "Converted 97_test_utils.ipynb.\n",
      "Converted 99_pytorch_doc.ipynb.\n",
      "Converted dev-setup.ipynb.\n",
      "Converted index.ipynb.\n",
      "Converted quick_start.ipynb.\n",
      "Converted tutorial.ipynb.\n"
     ]
    }
   ],
   "source": [
    "#hide\n",
    "from nbdev.export import notebook2script\n",
    "notebook2script()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "jupytext": {
   "split_at_heading": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
